#include <nxos.h>

NX_PRIVATE NX_Error test_framebuffer(void)
{
    NX_Solt fb0 = NX_DeviceOpen("fb0", 0);
    if (fb0 < 0) {
        NX_Printf("open fb device failed!\n");
        return NX_ENOSRCH;
    }
 
    NX_FramebufferInfo fbInfo;

    if (NX_DeviceControl(fb0, NX_FRAMEBUFFER_CMD_GETINFO, &fbInfo) != NX_EOK)
    {
        NX_Printf("get fb info failed!\n");
        NX_SoltClose(fb0);
        return NX_EPERM;
    }

    NX_Printf("fbinfo: bpp:%d, scanline:%d, x:%d, y:%d, base:%p\n", fbInfo.bitsPerPixel, fbInfo.bytesPerScanLine, fbInfo.xResolution, fbInfo.yResolution, fbInfo.phyBasePtr);

    int bytes_per_pixel = fbInfo.bitsPerPixel / 8;
    NX_Size fb_len = bytes_per_pixel * fbInfo.yResolution * fbInfo.xResolution; 
    void *fb_buf = NX_DeviceMap(fb0, fb_len, NX_PROT_READ | NX_PROT_WRITE);
    if (fb_buf == NX_NULL) {
        NX_Printf("device map failed! %s\n", NX_ErrorToString(NX_ErrorGet()));
        NX_SoltClose(fb0);
        return NX_EPERM;
    }
    NX_TimeVal time1 = 0;
    NX_TimeVal time2 = 0;
    int fps = 0;
    int total_fps = 0;
    int test_count = 0;
    
    time1 = NX_ClockGetMillisecond();
    
    while (test_count < 10)
    {
        NX_MemSet(fb_buf, test_count * 10, fb_len);

        fps++;
        time2 = NX_ClockGetMillisecond();
        unsigned long long mtime = (time2 - time1);
        if (mtime > 1000) {
            NX_Printf("fps %d\n", fps);
            total_fps += fps;
            fps = 0;
            time1 = time2;
            test_count++;
        }
    }
    NX_Printf("total fps:%d , per fps in 10s:%d\n", total_fps, total_fps / 10);
    
    NX_MemUnmap(fb_buf, fb_len);

    NX_SoltClose(fb0);
    NX_Printf("framebuffer test done!\n");
    return NX_EOK;
}

NX_PRIVATE NX_Error test_keyboard(void)
{
    NX_Solt key0 = NX_DeviceOpen("keyboard0", 0);
    if (key0 < 0) {
        NX_Printf("open keyboard device failed!\n");
        return NX_ENOSRCH;
    }

    NX_U32 leds;

    NX_Error err;

    if ((err = NX_DeviceControl(key0, NX_INPUT_EVENT_CMD_GETLED, &leds)) != NX_EOK)
    {
        NX_Printf("get keyboard info failed with err %s!\n", NX_ErrorToString(err));
        NX_SoltClose(key0);
        return NX_EPERM;
    }

    NX_Printf("keyboard led: %x\n", leds);
    NX_Printf("keyboard please input keyboard:\n");

    NX_InputEvent e;
    NX_TimeVal time1 = 0;
    NX_TimeVal time2 = 0;
    int test_count = 0;
    
    time1 = NX_ClockGetMillisecond();
    
    while (test_count < 10)
    {
        time2 = NX_ClockGetMillisecond();
        unsigned long long mtime = (time2 - time1);
        if (mtime > 1000) {
            time1 = time2;
            test_count++;
        }

        if (NX_DeviceRead(key0, &e, 0, sizeof(e)) > 0)
        {
            if (e.type == NX_EV_KEY)
            {
                if (e.value == 1)
                {
                    NX_Printf("keydown: %x/%c\n", e.code, e.code);
                }
                else
                {
                    NX_Printf("keyup: %x/%c\n", e.code, e.code);
                }
            }
        }
    }
    NX_SoltClose(key0);
    NX_Printf("keyboard test done!\n");
    return NX_EOK;
}

NX_PRIVATE NX_Error test_mouse(void)
{
    NX_Solt dev = NX_DeviceOpen("mouse0", 0);
    if (dev < 0) {
        NX_Printf("open mouse device failed!\n");
        return NX_ENOSRCH;
    }

    NX_Printf("mouse please input mouse:\n");

    NX_InputEvent e;
    NX_TimeVal time1 = 0;
    NX_TimeVal time2 = 0;
    int test_count = 0;
    
    time1 = NX_ClockGetMillisecond();
    
    while (test_count < 10)
    {
        time2 = NX_ClockGetMillisecond();
        unsigned long long mtime = (time2 - time1);
        if (mtime > 1000) {
            time1 = time2;
            test_count++;
        }

        if (NX_DeviceRead(dev, &e, 0, sizeof(e)) > 0)
        {
            if (e.type == NX_EV_KEY)
            {
                if (e.value == 1)
                {
                    switch (e.code)
                    {
                    case NX_BTN_MOUSE_LEFT:
                        NX_Printf("mouse left keydown: %x/%c\n", e.code, e.code);
                        break;
                    case NX_BTN_MOUSE_MIDDLE:
                        NX_Printf("mouse middle keydown: %x/%c\n", e.code, e.code);
                        break;
                    case NX_BTN_MOUSE_RIGHT:
                        NX_Printf("mouse right keydown: %x/%c\n", e.code, e.code);
                        break;
                    
                    default:
                        break;
                    }
                }
                else
                {
                    switch (e.code)
                    {
                    case NX_BTN_MOUSE_LEFT:
                        NX_Printf("mouse left keyup: %x/%c\n", e.code, e.code);
                        break;
                    case NX_BTN_MOUSE_MIDDLE:
                        NX_Printf("mouse middle keyup: %x/%c\n", e.code, e.code);
                        break;
                    case NX_BTN_MOUSE_RIGHT:
                        NX_Printf("mouse right keyup: %x/%c\n", e.code, e.code);
                        break;
                    
                    default:
                        break;
                    }
                }
            }
            else if (e.type == NX_EV_REL)
            {
                if (e.code == NX_REL_X)
                {
                    NX_Printf("mouse motion x: %x/%d\n", e.value, e.value);
                }
                else if (e.code == NX_REL_Y)
                {
                    NX_Printf("mouse motion y: %x/%d\n", e.value, e.value);
                }
                else if (e.code == NX_REL_WHEEL)
                {
                    NX_Printf("mouse wheel: %x/%d\n", e.value, e.value);
                }
            }
        }
    }
    NX_SoltClose(dev);
    NX_Printf("mouse test done!\n");
    return NX_EOK;
}

NX_PRIVATE NX_Error test_function(void)
{
    NX_Size len;

    NX_Solt dev = NX_DeviceOpen("null", 0);
    if (dev < 0)
    {
        NX_Printf("device null\n");
        return NX_ERROR;
    }

    char buf[32];
    len = NX_DeviceWrite(dev, buf, 0, 32);
    NX_Printf("device write len:%d\n", len);
    
    NX_DeviceClose(dev);

    NX_Solt zeroDev = NX_DeviceOpen("zero", 0);
    if (zeroDev < 0)
    {
        NX_Printf("device null\n");
        return NX_ERROR;
    }

    len = NX_DeviceRead(zeroDev, buf, 0, 32);
    NX_Printf("device read len:%d\n", len);

    NX_Error err = NX_DeviceControl(zeroDev, 0, NX_NULL);
    NX_Printf("device control err:%d\n", err);

    NX_DeviceClose(zeroDev);
    return NX_EOK;
}

NX_PRIVATE NX_Error test_udriver(void)
{
    NX_Size len;

    NX_Solt dev = NX_DeviceOpen("udriver_test0", 0);
    if (dev < 0)
    {
        NX_Printf("device null\n");
        return NX_ERROR;
    }

    char buf[32];
    int i;
    
    for (i = 0; i < 32; i++)
    {
        buf[i] = 32 - i;
    }
    len = NX_DeviceWrite(dev, buf, 0, 32);
    NX_Printf("device write len:%d err:%d\n", len, NX_ErrorGet());
    
    len = NX_DeviceRead(dev, buf, 0, 32);
    NX_Printf("device read len:%d err:%d\n", len, NX_ErrorGet());

    for (i = 0; i < len; i++)
    {
        NX_Printf("device read:%x\n", buf[i]);
    }

    NX_Error err = NX_DeviceControl(dev, 0, NX_NULL);
    NX_Printf("device control err:%d\n", err);

    void * map = NX_DeviceMap(dev, 0x1000, NX_PROT_READ | NX_PROT_WRITE);
    NX_Printf("device map addr %p err:%d\n", map, NX_ErrorGet());

    NX_DeviceClose(dev);

    return NX_EOK;
}

NX_PRIVATE NX_Error test_pmem(void)
{
    NX_Size len;

    NX_Solt dev = NX_DeviceOpen("pmem", 0);
    if (dev < 0)
    {
        NX_Printf("device null\n");
        return NX_ERROR;
    }

    char buf[32];
    len = NX_DeviceRead(dev, buf, 0x100000, 32);
    NX_Printf("device read len:%d\n", len);

    int i;
    
    for (i = 0; i < 32; i++)
    {
        NX_Printf("read byte:%x\n", buf[i]);
    }

    len = NX_DeviceWrite(dev, buf, 0x100000, 32);
    NX_Printf("device write len:%d\n", len);

    NX_Error err = NX_DeviceControl(dev, NX_PMEM_CMD_SETADDR, (void *)0x100000);
    NX_Printf("device control err:%d\n", err);

    char *map = NX_DeviceMap(dev, 0x1000, NX_PROT_READ | NX_PROT_WRITE);

    for (i = 0; i < 32; i++)
    {
        buf[i] = map[i];
    }

    for (i = 0; i < 32; i++)
    {
        map[i] = buf[i];
    }

    NX_MemUnmap(map, 0x1000);

    NX_DeviceClose(dev);
    return NX_EOK;
}

NX_Error TEST_Device(char *cmdline)
{
    test_pmem();
    test_udriver();
    test_function();
    test_framebuffer();
    test_mouse();
    test_keyboard();
    return NX_EOK;
}
